package gerenciaDados.cln.cgt.PI.descricaoeRepresentacao;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.imageio.ImageIO;

public class Fourier {

	private BufferedImage imgborda;
	private float [][] assinatura;	
	
	public Fourier(BufferedImage imgbw,int N){			
		this.imgborda = this.deteccaodeBorda(imgbw);		
		this.assinatura = this.geraAssinatura(imgborda, N);	
	}
	
	private BufferedImage deteccaodeBorda(BufferedImage img){
		int largura = img.getWidth();
		int altura = img.getHeight();
		int index;					
		int [] pixels = img.getRGB(0, 0, largura, altura, null, 0, largura);
		BufferedImage imgb;
		
		// Borda esquerda
		l1:
		for (int lin = 0; lin < altura; lin++) {
			for (int col = 0; col < largura; col++) {
				index = largura * lin + col;				
				if(new Color(pixels[index]).equals(Color.WHITE)){
					pixels[index] = Color.GREEN.getRGB();					
					continue l1;
					
				}
				
			}
		}
		
		// Borda direita
		l2:
		for (int lin = 0; lin < altura; lin++) {
			for (int col = largura-1; col >= 0; col--) {
				index = largura * lin + col;				
				if(new Color(pixels[index]).equals(Color.WHITE)){
					pixels[index] = Color.GREEN.getRGB();
					continue l2;
				}
				
			}
		}
		
		// Borda superior
		l3:
		for (int col=0; col < largura; col++) {
				for (int lin = 0; lin < altura; lin++) {
				index = largura * lin + col;				
				if(new Color(pixels[index]).equals(Color.WHITE)){
					pixels[index] = Color.GREEN.getRGB();
					continue l3;
				}
				
			}
		}
		
		// Borda superior
		l4:
		for (int col=0; col < largura; col++) {
				for (int lin = altura-1; lin >= 0; lin--) {
				index = largura * lin + col;				
				if(new Color(pixels[index]).equals(Color.WHITE)){
					pixels[index] = Color.GREEN.getRGB();
					continue l4;
				}
				
			}
		}
		
		// Limpar
		for (int lin = 0; lin < altura; lin++) {
			for (int col = 0; col < largura; col++) {
				index = largura * lin + col;				
				if(new Color(pixels[index]).equals(Color.WHITE)){
					pixels[index] = Color.BLACK.getRGB();
					
				}							
				if(new Color(pixels[index]).equals(Color.GREEN)){
					pixels[index] = Color.WHITE.getRGB();
					
				}
			}
		}
		
		imgb = new BufferedImage(largura, altura, img.getType());
		imgb.setRGB(0, 0, largura, altura, pixels, 0, largura);
		return imgb;
	}
	
	public float[][] geraAssinatura(BufferedImage im, int qmax){
		int altura,
		largura, 
		i,
		j,
		xc,
		yc,
		x,
		y,		
		q,
		n;
		int [] pixels;	
		double ang, arg_xc=0.0, arg_yc=0.0;
		float [][] b = new float[qmax][2];
		
		altura = im.getHeight();
		largura = im.getWidth();
		pixels = im.getRGB(0, 0, largura, altura, null, 0, largura);
		
		n=0;
		for(i=0; i< altura; i++){
			for(j=0; j<largura;j++){
				if( pixels[i*largura + j] == Color.WHITE.getRGB()){
					arg_xc += j;
					arg_yc += i;
					n++;
				}
			}
		}
		arg_xc /= n;
		arg_yc /= n;
				
		xc= (int) arg_xc;
		yc= (int) arg_yc;					
		
		for(q=0; q<qmax;q++){
			ang = q*(2*Math.PI/qmax);
			i=1;
			x = (int)((float)xc + i*Math.cos(ang));
			y = (int)((float)yc + i*Math.sin(ang));
			b[q][0] = -1;
			b[q][1] = -1;		
			
			while(!(x<0 || x>=largura || y<0 || y>=altura)){					
				if(pixels[y*largura + x] == Color.WHITE.getRGB()){					
					b[q][0] = x;
					b[q][1] = y;
					break;
				}																							
				i++;
				x = (int)((float)xc + i*Math.cos(ang));
				y = (int)((float)yc + i*Math.sin(ang));					
			}			
		}			
			
		return b;
	}
	
	public double [] extracao(int K)
	{		
		int N = this.assinatura.length;
		Ponto [] p = new Ponto[N];
		Ponto [] AK = new Ponto[K-1];
		Ponto [] BK = new Ponto[K-1];
		
		//Cria um vetor de Pontos
		for(int i=0; i<N; i++){
			p[i] = new Ponto(this.assinatura[i][0], this.assinatura[i][1]);					
		}
		
		int head,tail,centro,spanback,spanfoward,span,i;
		centro=0;
		while(centro < N)
		{
			if(p[centro].getX() == -1 &&  p[centro].getY() == -1)
			{
				head = centro;
				tail = centro;
				spanback=0;
				spanfoward=0;
				while(p[head].getX() == -1 &&  p[head].getY() == -1)
				{
					head = (head == N-1 ? 0 : head+1);
					spanfoward++;
				}
				
				while(p[tail].getX() == -1 &&  p[tail].getY() == -1)
				{
					tail = (tail == 0 ? N-1 : tail-1);
					spanfoward++;
				}
				span = spanback + spanfoward;
				i=1;
				while( i < span)
				{
					p[(tail+i)%N].setX((-i*p[tail].getX() + p[tail].getX()*span + p[head].getX()*i)/span);
					p[(tail+i)%N].setY((-i*p[tail].getY() + p[tail].getY()*span + p[head].getY()*i)/span);
					i++;
				}
			}
			centro++;
		}
		
		double AX0=0.0, AY0=0.0;
		for(int n=0; n< N; n++){
			AX0 += p[n].getX();
			AY0 += p[n].getY();
		}
		AX0 /= N;
		AY0 /= N;
		
		for(int n=0; n< N; n++){
			p[n].setX(p[n].getX() - AX0);
			p[n].setY(p[n].getY() - AY0);
		}

		for(int j=1; j<K; j++){				
			AK[j-1] = new Ponto(A(j, N, p,'X'),A(j, N, p,'Y'));
			BK[j-1] = new Ponto(B(j, N, p,'X'),B(j, N, p,'Y'));
		}

		double MgA;
		double MgB;
		double [] FD = new double[K-1];
		double [] carac = new double[K-2];
		
		for(i=0; i<K-1;i++)
		{
			MgA = Math.sqrt( Math.pow(AK[i].getX() , 2) + Math.pow(AK[i].getY(), 2) );
			MgB = Math.sqrt( Math.pow(BK[i].getX() , 2) + Math.pow(BK[i].getY(), 2) );
			FD[i] = Math.sqrt( Math.pow(MgA, 2)  + Math.pow(MgB, 2) );			
		}
		
		//Fourier.DEBUGOCTAVE(p,AK,BK,N,K);
		
		for(i=0; i<K-2;i++)
		{			
			carac[i] = FD[i+1]/FD[0];			
		}			
		
		return carac;
	}
	
	private static double A(int j, int N, Ponto [] p,char eixo){
		double acm=0.0;
		if(eixo == 'X'){
			for(int i=0; i<N; i++){
				acm += p[i].getX()*Math.sin(i*(((double)j/(double)N)*2*Math.PI) );
			}
		}else if(eixo == 'Y'){
			for(int i=0; i<N; i++){
				acm += p[i].getY()*Math.sin(i*(((double)j/(double)N)*2*Math.PI) );
			}	
		}
		return ((double)2/(double)N)*acm;
	}
	
	private static double B(int j, int N, Ponto [] p, char eixo){
		double acm=0.0;
		if(eixo == 'X'){
			for(int i=0; i<N; i++){
				acm += p[i].getX()*Math.cos(i*(((double)j/(double)N)*2*Math.PI) );
			}
		}else if(eixo == 'Y'){
			for(int i=0; i<N; i++){
				acm += p[i].getY()*Math.cos(i*(((double)j/(double)N)*2*Math.PI) );
			}	
		}
		return ((double)2/(double)N)*acm;
	}
}

